#include "kzfhoughcircledetect.h"


kzfHoughCircleDetect::kzfHoughCircleDetect()
{

}

/*
threshold--阈值
dir 0-像素点累计当值比threshold小   1-像素点累计当值比threshold大
适合一个图象一个图象的计算
*/
int kzfHoughCircleDetect::countPixel(Mat& src, int threshold, bool dir)
{
    int pixelsNum = 0;
    int i,j,c;

    // qDebug()<<src.rows <<src.cols<<src.at<Vec3b>(1,1)[0];
    // c = src.channels();

    //要去掉一些边界值,如果传入的图象边边模糊过
    for(i=3;i<src.rows-3;i++){
        for(j=3;j<src.cols-3;j++){
            if(dir==0){
                if(src.at<Vec3b>(i,j)[0] < threshold){
                    pixelsNum++;
                }
            }else{
                if(src.at<Vec3b>(i,j)[0] > threshold){
                    pixelsNum++;
                }
            }
        }
    }
    // qDebug() <<"count real pixels:"<< pixelsNum;
    return pixelsNum;
}

/*
输入二值图象-src
图象的边框-Rect
返回总像素的平均值
根据圆形的值,分别向上下,左右计算总像素点,做平均
Mat 坐标
    row == heigh == Point.y
    col == width == Point.x
    Mat::at(Point(x, y)) == Mat::at(y,x)
*/
int kzfHoughCircleDetect::getCirclePixel(Mat& src, RotatedRect Rect)
{
    int i,j;
    int centerX, centerY, cR, maxheight;//圆心坐标,半径
    int area;
    uchar color;//圆形的值
    uchar *lineD;

    bool dLift = 0, dRight = 0;

    if(src.channels()>1){
        qDebug()<<"img channal err too big";
        return 0;
    }

    centerX = Rect.center.x;
    centerY = Rect.center.y;
    cR = (Rect.size.height + Rect.size.width)/4 + 4;

    //cv::Mat中提供ptr函数访问任意一行像素的首地址
    lineD = src.ptr<uchar>(centerY);//获取圆形行的首地址
    color = lineD[centerX];//获取圆心的值
    maxheight = src.size().height;

    area=0;//
    for(i=0; i<cR; i++){
        if(centerY - i >= 0){ //上半圆计算
            lineD = src.ptr<uchar>(centerY - i);//获取行指针
            dLift = 0, dRight = 0;
            for (int j = 0; j<cR; j++){
                if(!dRight && lineD[centerX+j]==color){//一行向后数
                    area++;
                }else{
                    dRight = 1;
                }

                if(j>0){
                    if(!dLift && lineD[centerX-j]==color){//一行向前数
                        area++;
                    }else{
                        dLift = 1;
                    }
                }
                if(dLift && dRight){
                    break;
                }
            }
        }

        if(i>0 && (centerY + i < maxheight)){             //下半圆计算 不计算直径
            lineD = src.ptr<uchar>(centerY + i);        //获取行指针
            dLift = 0, dRight = 0;
            for (int j = 0; j<cR; j++){
                if(!dRight && lineD[centerX+j]==color){//一行向后数
                    area++;
                }else{
                    dRight = 1;
                }

                if(j>0){
                    if( !dLift && lineD[centerX-j]==color){//一行向前数
                        area++;
                    }else{
                        dLift = 1;
                    }
                }
                if(dLift && dRight){
                    break;
                }
            }
        }
    }

    return area;
}

void kzfHoughCircleDetect::imageBasicInformation(Mat& src)//图像基本信息
{

    int cols = src.cols;
    int rows = src.rows;
    int channels = src.channels();

    cout << "img width" << cols << endl;
    cout << "img hight" << rows << endl;
    cout << "img channels" << channels << endl;
}

Mat kzfHoughCircleDetect::houghCirclePre(Mat& srcPre)        //霍夫圆检测预处理
{

    Mat erode_threshold_graySrc;
    Mat graySrc, threshold_grayaSrc, dilate_threshold_graySrc;

    int debugPixels = 0;

    cvtColor(srcPre, graySrc, COLOR_BGR2GRAY);
    debugPixels = countPixel(graySrc, 200, 1);
    qDebug()<<"after cvtColor:"<< debugPixels;


    //高斯滤波增大误差太多
    // GaussianBlur(graySrc, graySrc, Size(3, 3), 2, 2);       //滤波 这个参数去掉点太多
    // debugPixels = countPixel(graySrc, 200, 1);
    // qDebug()<<"after gauss:"<< debugPixels;

    threshold(graySrc, threshold_grayaSrc, 150, 255, 1);    //二值化
    debugPixels = countPixel(threshold_grayaSrc, 200, 1);
    qDebug()<<"after threshold:"<< debugPixels;

    imshow("hough preteament", threshold_grayaSrc);

    Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
    dilate(threshold_grayaSrc, dilate_threshold_graySrc, element);      //膨胀
    erode(dilate_threshold_graySrc, erode_threshold_graySrc, element);  //腐蚀
    debugPixels = countPixel(erode_threshold_graySrc, 200, 1);
    qDebug()<<"after erode:"<< debugPixels;

    imshow("hough erode_threshold_graySrc", erode_threshold_graySrc);

    // return threshold_grayaSrc;
    return erode_threshold_graySrc;
}

void kzfHoughCircleDetect::houghCircle(Mat& srcPreHough, Mat& src)     //霍夫圆检测
{
    cout << "into hough detect" << endl;

    vector<Vec3f> circles;

    // InputArray image,
    // OutputArray circles,
    // int method,
    // double dp,
    // double minDist,
    // double param1 = 100,
    // double param2 = 100,
    // int minRadius = 0,
    // int maxRadius = 0
    HoughCircles(srcPreHough, circles, HOUGH_GRADIENT, 1, 60, 1, 35, 0, 0);
    // HoughCircles(srcPreHough, circles, HOUGH_GRADIENT, 1, 60);

    cout << "-------------circle nums:" << circles.size() << endl;
    for (size_t i = 0;i < circles.size();i++){
        Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);        //画圆心
        circle(src, center, radius, Scalar(0, 0, 255), 1, 8, 0);    //画圆
    }

    imshow("hough reselt", src);
}


/*
    输出二值图象
*/
Mat kzfHoughCircleDetect::RectCirclePre(Mat& srcPre)         //面积筛选拟合圆的预处理
{
    Mat graySrcArea, thresholdGraySrc, dilateThresholdGraySrc, erodeThresholdGraySrc;

    int debugPixels = 0;

    cvtColor(srcPre, graySrcArea, COLOR_BGR2GRAY);
        // debugPixels = countPixel(graySrcArea, 200, 1);
        // qDebug()<<"after cvtColor:"<< debugPixels;

    //------------基本的二值图片基本不用高斯滤波--------------------
    // GaussianBlur(graySrcArea, graySrcArea, Size(3, 3), 2, 2);//滤波太大,误差增大好多
    // boxFilter(graySrcArea, graySrcArea, -1, Size(3, 3));//滤波太大,误差增大好多
    // blur(graySrcArea, graySrcArea, Size(3,3));//滤波太大,误差增大好多
    // medianBlur(graySrcArea, graySrcArea, 7);//这个影响不大
    bilateralFilter(graySrcArea, thresholdGraySrc, 10, 10*2, 10/2);//这个影响不大
    graySrcArea = thresholdGraySrc.clone();
        // debugPixels = countPixel(graySrcArea, 200, 1);
        // qDebug()<<"after gauss-:"<< debugPixels;
        // imshow("RectCirclePre graySrcArea", graySrcArea);

    threshold(graySrcArea, thresholdGraySrc, 200, 255, THRESH_BINARY);//
        // debugPixels = countPixel(thresholdGraySrc, 200, 1);
        // qDebug()<<"after threshold:"<< debugPixels;
        // imshow("RectCirclePre threshold", thresholdGraySrc);

#if 0//把小边沿全部搞无了
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(25, 25));//Size(25, 25)核太小增大误差,MORPH_ELLIPSE匹配圆误差小
    dilate(thresholdGraySrc, dilateThresholdGraySrc, element);//膨胀
    erode(dilateThresholdGraySrc, erodeThresholdGraySrc, element);//腐蚀
        debugPixels = countPixel(erodeThresholdGraySrc, 200, 1);
        qDebug()<<"after erode:"<< debugPixels;

    return erodeThresholdGraySrc;
#endif
    return thresholdGraySrc;
}

/*
    拟合圆 根据面积 半径
    AreaInput 是一个二值图象
    src  源图象
    输出圆的坐标列表 QList<kzfCirclebox>
*/
QList<kzfCirclebox> kzfHoughCircleDetect::AreaCircles(Mat& AreaInput, Mat &src)       //面积筛选拟合圆检测
{
    vector<vector<Point>> RectContours;
    vector<Vec4i> Hierarchy;
    QList<kzfCirclebox> rect;

    // Mat imgZero(src.size(), src.type(), Scalar(0));

    // unsigned char R = 255;
    // unsigned char G = 100;
    // unsigned char B = 100;

    // imshow("AreaCircles", AreaInput);

    // InputOutputArray image,
    // OutputArrayOfArrays contours,
    // OutputArray hierarchy,
    // int mode,
    // int method,
    // Point offset = Point()

    // #RETR_EXTERNAL：最外层轮廓
    // #CV_RETR_LIST：检测所有的轮廓，包括内围、外围轮廓，但是检测到的轮廓不建立等级关系，彼此之间独立
    // #CV_RETR_CCOMP：检测所有的轮廓，但所有轮廓只建立两个等级关系，外围为顶层
    // #CV_RETR_TREE：检测所有轮廓，所有轮廓建立一个等级树结构

    // # CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
    // # CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息
    // # CV_CHAIN_APPROX_TC89_L1，CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

    // # contours:轮廓信息
    // # hierarchy：层级关系[2,0,-1,-1]，后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号
    findContours(AreaInput, RectContours, Hierarchy, RETR_TREE, CV_CHAIN_APPROX_TC89_KCOS, Point(0, 0));//CV_CHAIN_APPROX_TC89_KCOS  CV_CHAIN_APPROX_TC89_L1 得到结果更接近

    // Mat drawing = Mat::zeros(src.size(), CV_8UC3);
    // qDebug()<<"find contours"<<RectContours.size();

    double areaMin = 640*480/50, areaMax = 640*480*3/4;//限制圆的大小,最小总面积的1/10  最大是总面积的 3/4

    for(int i = 0; i < RectContours.size(); i++){
        double area = contourArea(RectContours[i]);//计算面积
        double isLikeCircle = 0;
        double isLikeArea = 0;

        kzfCirclebox kzfRect;

        // qDebug() << i <<":area:"<<area;//输出所有计算出来的面积，方便下一步设置阈值
        // drawContours(drawing, RectContours, i, Scalar(0, 255, 0), 2,8, Hierarchy, 0, Point());
        // imshow("drawing", drawing);

        if(area>areaMin && area<areaMax){
            RotatedRect Rect = fitEllipse(RectContours[i]);

            //判断是否为圆形
            isLikeCircle = Rect.size.height - Rect.size.width;//判断外框是否为方形

            // qDebug()<<"chect circleL:"<<isLikeCircle<<Rect.size.height<<Rect.size.width;
            if(isLikeCircle<5 && isLikeCircle>-5){
                isLikeArea = area / (3.1415926 * Rect.size.height * Rect.size.height / 4);//判断实际面积与理论面积 相似度95%以上
                // qDebug()<<"chect isLikeArea :"<<isLikeArea<<area<<3.1415926 * Rect.size.height * Rect.size.height / 4;
                if(isLikeArea>0.95 && isLikeArea<1.05 ){                //相似度95%以上
                    // qDebug()<<"get a circle";
                    kzfRect.rect.setHeight(Rect.size.height);
                    kzfRect.rect.setWidth(Rect.size.width);
                    kzfRect.centerPoint.setX(Rect.center.x);
                    kzfRect.centerPoint.setY(Rect.center.y);
                    kzfRect.area = getCirclePixel(AreaInput, Rect);
                    // countPixel(AreaInput, 200, 1);
                    // qDebug()<<"get a circle area:"<<i<<kzfRect.area;
                    rect.append(kzfRect);
                }
            }
            // qDebug()<<"circle cneter:"<<Rect.center.x<<Rect.center.y;
            // qDebug()<<"circle cneter:"<<Rect.size.height<<Rect.size.width;
        }

        // circle(src, Rect.center, 2, Scalar(B, G, R), 2, 8, 0);    //在原图画出圆心
        // ellipse(src, Rect, Scalar(B, G, R), 1);                   //在原图画出轮廓

        // circle(imgZero, Rect.center, 2, Scalar(B, G, R), 2, 8, 0);    //在原图画出圆心
        // ellipse(imgZero, Rect, Scalar(B, G, R), 1);                   //在原图画出轮廓
    }

    // imshow("show draw", drawing);
    // imshow("area show", src);
    // imshow("zero show", imgZero);

    return rect;
}
